/************************************************************************
Copyright (c) 2010, 2013, C.R. All rights reserved. 

E-mail: crtoyou@gmail.com; crtoyou@163.com

You can redistribute it and/or modify it under the terms of the
GNU Lesser General Public License as published by the
Free Software Foundation version 3 of the License.

This source code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. if not, write to the Free Software Foundation, Inc., 
51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
************************************************************************/

#ifndef __CR_AUTO_INCREASABLE_POOL_H__
#define __CR_AUTO_INCREASABLE_POOL_H__

#include <map>
#include <set>
#include <vector>
#include <stack>
#include <crdefine.h>
#include <crdebug.h>
#include <crsync.h>

namespace cr {


template <typename type_object>
struct simple_wrapper_t // all members are static 
{
	typedef type_object* handle_t;
	static inline handle_t create() {return (handle_t)malloc(sizeof(type_object));}
	static inline void close(handle_t h) {free(h);}
	static inline handle_t invalid_handle() {return NULL;}
	static inline bool is_valid(handle_t h) {return h!=NULL;}
};

template <typename type_object>
struct object_wrapper_t // all members are static 
{
	typedef type_object* handle_t;
	static inline handle_t create() {return new type_object;}
	static inline void close(handle_t h) {delete h;}
	static inline handle_t invalid_handle() {return NULL;}
	static inline bool is_valid(handle_t h) {return h!=NULL;}
};

template <
typename type_wrapper
>
class objpool
{
private:
	//disable copy
	objpool(const objpool&);
	objpool& operator=(const objpool&);

public:
	typedef typename type_wrapper::handle_t handle_t;

	objpool() : maxsize(MAXINT) {}
	~objpool(void) { ASSERT(ready.empty() && inuse.empty()); }

	inline void limit(size_t nmax = MAXINT) { maxsize = nmax; }
	inline size_t alive() const { return inuse.size(); }

	void init(size_t size)
	{
		autolock al(&lock);
		ASSERT(size <= maxsize);
		for (size_t i=0; i<size; ++i)
			ready.push(type_wrapper::create());
		inuse.insert(type_wrapper::invalid_handle());
	}

	void clear()
	{
		autolock al(&lock);
		inuse.erase(type_wrapper::invalid_handle());
		ASSERT(inuse.empty());
		//assert but ignore if inuse is not empty, though unexpected. 
		//because the pinter may be in use by other object. 
		while (!ready.empty()) {
			type_wrapper::close(ready.top());
			ready.pop();
		}
	}

	void resize(size_t size)
	{
		ASSERT(size <= maxsize);
		long cnt = size - inuse.size();
		if (cnt > (long)ready.size()) {
			for (size_t i=ready.size(); i<cnt; ++i)
				ready.push(type_wrapper::create());
		}
		else {
			if (cnt < 0)
				cnt = 0;
			while ((long)ready.size() > cnt) {
				type_wrapper::close(ready.top());
				ready.pop();
			}
		}
	}

	handle_t lease(void)
	{
		if (inuse.size() > (size_t)maxsize)
			return type_wrapper::invalid_handle();
		handle_t handle = type_wrapper::invalid_handle();
		if (!ready.empty()) {
			inuse.insert(handle = ready.top());
			ready.pop();
		}
		if (!type_wrapper::is_valid(handle))
			inuse.insert(handle = type_wrapper::create());
		return handle;
	}

	void release(handle_t handle)
	{
		if (!type_wrapper::is_valid(handle))
			return;
		std::set<handle_t>::iterator it = inuse.find(handle);
		// ignore if the node not belongs to the pool. 
		if (inuse.end() != it) {
			ready.push(handle);
			inuse.erase(it);
		}
	}

	handle_t recreate(handle_t handle)
	{
		if (type_wrapper::is_valid(handle)) {
			type_wrapper::close(handle);
			std::set<handle_t>::iterator it = inuse.find(handle);
			// ignore if the node not belongs to the pool. 
			if (inuse.end() != it) 
				inuse.erase(it);
		}
		inuse.insert(handle = type_wrapper::create());
		return handle;
	}

	inline void resize_s(size_t size)
	{
		autolock al(&lock);
		resize(size);
	}

	inline handle_t lease_s(void)
	{
		autolock al(&lock);
		return lease();
	}

	inline void release_s(handle_t node)
	{
		autolock al(&lock);
		release(node);
	}

	inline handle_t recreate_s(handle_t handle)
	{
		autolock al(&lock);
		return recreate(handle);
	}

protected:
	std::set<handle_t> inuse;
	std::stack<handle_t, std::vector<handle_t>> ready;
	size_t maxsize;

protected:
	critical_section lock;
};


template <
size_t SIZ_UNIT
>
class mempool
{
public:
	mempool(bool ilkaon = true)
		: useilka(ilkaon), mem(NULL), len(0), cursor(0)
	{}

	~mempool()
	{
		ASSERT(!mem);
	}

	void init(size_t size)
	{
		autolock al(&lock);
		//one more unit for gap
		cursor = 0;
		len = (size+1)*SIZ_UNIT;
		mem = (char*)malloc(len);
		if (!mem) {
			len = 0;
			return;
		}
		ready.resize(size);
		for (size_t i=0; i<size; ++i) 
			ready[i] = true;
		//don't push the last unit for gap. 
	}

	void clear()
	{
		autolock al(&lock);
		if (useilka)
			clearilka();
		//clear the ready nodes
		//don't let ~pool() to free the memory in pool. 
		ready.clear();
		if (mem) {
			free(mem);
			mem = NULL;
		}
		cursor = 0;
	}

	char* lease(void)
	{
		char* ptr = NULL;
		if (cursor >= ready.size())
			cursor = 0;
		size_t temp = cursor;
		if (!ready.empty()) {
			for (; cursor<ready.size(); ++cursor) {
				if (ready[cursor]) {
					ptr = mem+cursor*SIZ_UNIT;
					ready[cursor] = false;
					break;
				}
			}
			if (!ptr) {
				for (cursor=0; cursor<temp; ++cursor) {
					if (ready[cursor]) {
						ptr = mem+cursor*SIZ_UNIT;
						ready[cursor] = false;
						break;
					}
				}
			}
		}
		ASSERT(!ptr || inpool(ptr));
		if (!ptr && useilka) {
			ilka.insert(ptr = (char*)malloc(SIZ_UNIT)); //malloc ilka
			TRACE(_T("pool is small. unit size:%d, unit count:%d, temp count:%d\n"), 
				SIZ_UNIT, ready.size(), ilka.size());
		}
		return ptr;
	}

	void release(char* ptr)
	{
		if (!ptr)
			return;
		size_t i = (ptr-mem)/SIZ_UNIT;
		if (inpool(ptr)) {
			if (i < ready.size())
				ready[i] = true;
		}
		else if (useilka) {
			std::set<char*>::iterator it = ilka.find(ptr);
			if (ilka.end() != it) {
				free(ptr);
				ilka.erase(it);
			}
		}
		//else ignore
	}

	// !! risk: release the memory in use may cause abort. 
	void clearilka()
	{
		ASSERT(useilka);
		std::set<char*>::iterator it = ilka.begin();
		for (; ilka.end() != it; ++it)
			if (*it && !inpool(*it))
				free(*it);
		ilka.clear();
	}

	inline bool inpool(const char* ptr) const 
	{
		return (ptr >= mem && ptr < mem+len);
	}

	inline char* lease_s(void)
	{
		autolock al(&lock);
		return lease();
	}

	inline void release_s(char* ptr)
	{
		autolock al(&lock);
		release(ptr);
	}

protected:
	std::set<char*> ilka;
	std::vector<bool> ready;
	const bool useilka;

protected:
	critical_section lock;

private:
	char* mem;
	size_t len;
	size_t cursor;
};


template <
	size_t MINI_SIZE = 8,        //8
	size_t TINY_SIZE = 1024/16,  //64
	size_t SMAL_SIZE = 1024/2,   //512
	size_t NORM_SIZE = 1024*1,   //1024
	size_t HUGE_SIZE = 1024*4    //4096
>
class memgroup
{
public:
	memgroup()
		: Mini_(false)
		, Tiny_(false)
		, Smal_(false)
		, Norm_(false)
		, Huge_(false)
	{}

	void init(
		size_t mini,
		size_t tiny,
		size_t smal,
		size_t norm,
		size_t huge)
	{
		autolock al(&lock);
		Mini_.init(mini);
		Tiny_.init(tiny);
		Smal_.init(smal);
		Norm_.init(norm);
		Huge_.init(huge);
	}

	void clear()
	{
		autolock al(&lock);
		Mini_.clear();
		Tiny_.clear();
		Smal_.clear();
		Norm_.clear();
		Huge_.clear();
	}

	char* lease(size_t size)
	{
		ASSERT(size > 0 && size < 1024*1024*1024);
		char* ptr = NULL;
		// small to big order. 
		if (size <= MINI_SIZE)
			ptr = Mini_.lease();
		else if (size <= TINY_SIZE)
			ptr = Tiny_.lease();
		else if (size <= SMAL_SIZE)
			ptr = Smal_.lease();
		else if (size <= NORM_SIZE)
			ptr = Norm_.lease();
		else if (size <= HUGE_SIZE)
			ptr = Huge_.lease();
		return (ptr ? ptr : (char*)malloc(size));
	}

	void release(char* ptr)
	{
		if (!ptr)
			return;
		else if (Mini_.inpool(ptr))
			Mini_.release(ptr);
		else if (Tiny_.inpool(ptr))
			Tiny_.release(ptr);
		else if (Smal_.inpool(ptr))
			Smal_.release(ptr);
		else if (Norm_.inpool(ptr))
			Norm_.release(ptr);
		else if (Huge_.inpool(ptr))
			Huge_.release(ptr);
		else 
			free(ptr);
	}

	char* lease_s(size_t size)
	{
		autolock al(&lock);
		return lease(size);
	}

	void release_s(char* ptr)
	{
		autolock al(&lock);
		release(ptr);
	}

public:
	mempool<MINI_SIZE> Mini_;
	mempool<TINY_SIZE> Tiny_;
	mempool<SMAL_SIZE> Smal_;
	mempool<NORM_SIZE> Norm_;
	mempool<HUGE_SIZE> Huge_;

protected:
	critical_section lock;
};

template <
size_t MAX_COUNT,
typename type_data, 
typename type_id = int
>
class idpool
{
public:
	static const type_id invalid_id = (type_id)-1;

public:
	idpool() {idmap[invalid_id] = NULL;}

	type_id push(type_data& data)
	{
		if (idmap.size() >= MAX_COUNT)
			return invalid_id;
		while (idmap.find(idcursor) != idmap.end())
			++idcursor;
		idmap[idcursor] = data;
		ASSERT(invalid_id != idcursor);
		return idcursor;
	}

	bool pushat(type_id id, type_data& data)
	{
		ASSERT(invalid_id != id);
		if (invalid_id == id)
			return false;
		typemap::iterator it = idmap.find(id);
		if (idmap.end() != it)
			return false;
		idmap[id] = data;
		return true;
	}

	bool pop(type_id id, type_data& data)
	{
		if (invalid_id == id)
			return false;
		typemap::iterator it = idmap.find(id);
		if (idmap.end() != it) {
			data = it->second;
			idmap.erase(it);
			return true;
		}
		return false;
	}

	bool lookup(type_id id, type_data& data) const 
	{
		ASSERT(invalid_id != id);
		if (invalid_id == id)
			return false;
		typemap::const_iterator it = idmap.find(id);
		if (idmap.end() != it) {
			data = it->second;
			return true;
		}
		return false;
	}

	inline void rewind(type_id id)
	{
		idcursor = id;
	}

	inline type_id push_s(type_data& data)
	{
		autolock al(&lock);
		return push(data);
	}

	inline bool pushat_s(type_id id, type_data& data)
	{
		autolock al(&lock);
		return pushat(id, data);
	}

	inline bool pop_s(type_id id, type_data& data)
	{
		autolock al(&lock);
		return pop(id, data);
	}

	inline bool lookup_s(type_id id, type_data& data) const 
	{
		autolock al(&lock);
		return lookup(id, data);
	}

	inline void rewind_s(type_id id)
	{
		autolock al(&lock);
		rewind(id);
	}

protected:
	// typemap is provided to derived class to enumerate the elements
	// don't public it to outside
	typedef std::map<type_id, type_data> typemap;
	typemap idmap;
	type_id idcursor; //any value unique in map

protected:
	critical_section lock;
};

}//namespace

#endif //__CR_AUTO_INCREASABLE_POOL_H__